/*
 * ***** BEGIN LICENSE BLOCK *****
 * This file is part of "myWiWall".
 * Copyright (c) 2007-2008 CRP Henri Tudor and contributors.
 * All rights reserved.
 *
 * "myWiWall" is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 * 
 * "myWiWall" is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with "myWiWall"; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * ***** END LICENSE BLOCK *****
 */


/*
 * EventDispatcher class.
 */



// Single instance to be called everywhere... (not clean, but I can not make a true Singleton in JS)
var eventDispatcher = new EventDispatcher();

function EventDispatcher()
{
	// The map that will contain all the listeners handled by the portal
	this.listeners = new Object();
}

/**
 * @param listener		String	Widget identifier of a widget that wants to register for a specific event type
 * @param eventType		String	The type of the event
 * @param eventHandler	Function A function that will be called when the listener widget will receive an event of eventType
 * @param eventSrce		string	Widget identifier of widget(s) from which the listener wants to receive events. Can be omitted or set to null if the source does not matter.
 */
EventDispatcher.prototype.addWidgetEventListener = function(listener, eventType, eventHandler, eventSrc) 
{
	
	// -- Single listening Handling.
	if (eventSrc && typeof eventSrc == 'string')
	{
		// Add a new event to listen for the widget.
		if (!this.listeners[listener])
		{
			// The widget do not listen to anything at this time.
			this.listeners[listener] = new Object();
			this.listeners[listener][eventType] = {acceptAll: false, acceptedSources: [{eventSrc: eventSrc, eventHandler: eventHandler}]};		}
		else
		{
			// The widget already listen to something.
			if (!this.listeners[listener][eventType])
			{
				// First time that the widget registers for this type of event.
				this.listeners[listener][eventType] = {acceptAll: false, acceptedSources: [{eventSrc: eventSrc, eventHandler: eventHandler}]};
			}
			else
			{
				// We have to check if the widget already accept the source.
				// If not we add the new accepted source.
				var isMultiCast = this.listeners[listener][eventType].acceptAll;
				
				if (!isMultiCast)
				{
					// The widget was listening in multicast mode.
					var acceptedSources 	= this.listeners[listener][eventType].acceptedSources;
					var sourceCount 		= acceptedSources.length;
					
					for (var i = 0; i < sourceCount; i++)
					{
						if (acceptedSources[i].eventSrc == eventSrc)
							break;
					}
					
					if (i < sourceCount)
					{
						// The widget was already listening for these event and source.
						// So we replace the handler by the new one.
						acceptedSources[i].eventHandler = eventHandler;
					} 
					else
					{
						// The widget was not listening for this event and source.
						acceptedSources.push({eventSrc: eventSrc, eventHandler: eventHandler});
					}
				}
				else
				{
					// The widget was listening in multicast. The strategy here is to remove the multicast binding
					// and to set the listening to the new single event.
					var eventTypeContainer = this.listeners[listener][eventType];
					
					eventTypeContainer.acceptAll = false;
					eventTypeContainer.acceptedSources = [{eventSrc: eventSrc, eventHandler: eventHandler}];
				}
			}
		}
	}
	// -- Multiple listening Handling.
	else if (!eventSrc)
	{
		if (!this.listeners[listener])
			// The first time that the widget listen for this event in multiple listening mode.
			this.listeners[listener] = new Object();
		
		if (this.listeners[listener] && this.listeners[listener][eventType] && this.listeners[listener][eventType].acceptedSources)
			delete this.listeners[listeners][eventType].acceptedSources;
		
		// We override all accepted sources for this event and widget.
		this.listeners[listener][eventType] = {acceptAll: true, eventHandler: eventHandler};
	}
	
	// else fail silently ...
}
	
EventDispatcher.prototype.removeWidgetEventListener = function(listener, eventType, eventSrc) 
{
	if (this.listeners[listener] && this.listeners[listener][eventType])
	{
		var listeningCollection = this.listeners[listener][eventType];
		
		if (eventSrc)
		{
			// -- Single listening handling.
			
			// Was it listening in multiple listening mode ?
			if (listeningCollection.acceptAll)
			{
				// We have to chose a behavior when the widget was in multiple listening
				// mode for this event but request to stop listening for a given widget source.
				// At the moment, we just say that it stop listening for everything.
				this.listeners[listener][eventType] = undefined;
				delete listeningCollection;
			}
			else
			{
				// We have to find the listening and remove it.
				acceptedSources = listeningCollection.acceptedSources;
				var i = 0;
				
				for (i; i < acceptedSources.length; i++)
				{
					if (acceptedSources[i].eventSrc == eventSrc)
						break;
				}
				
				// If we found it, we simply delete the listening.
				if (i < acceptedSources.length)
				{
					acceptedSources.splice(i, 1);
				}
			}
		}
		// -- Multiple listening handling.
		{
			this.listeners[listener][eventType] = undefined;
			delete listeningCollection;
		}
	}
}
	
/**
 * @param source string id of the widget that fires the event
 * @param eventType String The type of the event to be sent
 * @param data * Anything that needs to be transmitted (String, Object, ...)
 * @param target String Widget instance that will receive the event in case of unicast. Can be omitted or set to null for broadcast
 */
EventDispatcher.prototype.fireWidgetEvent = function(source, eventType, eventData, target) 
{
	// -- Unicast handling.
	if (target && typeof target == 'string' && this.listeners[target] && this.listeners[target][eventType])
	{
		this.handleFiring(this.listeners[target][eventType], source, eventType, eventData);
	}
	// -- Multicast handling.
	else if (target && typeof target == 'Array')
	{
		for(t in target)
		{
			if (this.listeners[t] && this.listeners[t][eventType])
				this.handleFiring(this.listeners[t][eventType], source, eventType, eventData);
		}
	}
	// --Broadcast handling.
	else if (!target)
	{
		handlers = new Array();
		
		for (var listener in this.listeners)
		{
			if (this.listeners[listener] && this.listeners[listener][eventType] && this.listeners[listener][eventType].acceptAll == true)
			{
				handlers.push(this.listeners[listener][eventType].eventHandler);
			}
			else if (this.listeners[listener] && this.listeners[listener][eventType] && this.listeners[listener][eventType].acceptAll == false)
			{
				// We have to look up if it accepts to listen this source.
				sources = this.listeners[listener][eventType].acceptedSources;
				for (i = 0; i < sources.length; i++)
				{
					if (sources[i]['eventSrc'] == source)
						handlers.push(sources[i]['eventHandler']);
				}
			}
		}
		
		// Event firing.
		for (var i = 0; i < handlers.length; i++)
		{
			handlers[i]({eventType: eventType, eventData: eventData});
		}
	}
}

EventDispatcher.prototype.handleFiring = function(eventsForTarget, source, eventType, eventData)
{
	wantReceive = false;
	handler = null;
	
	if (eventsForTarget && eventsForTarget.acceptAll == true)
	{
		
		wantReceive = true;
		handler = eventsForTarget.eventHandler;
	}
	else
	{
		// We have to look up if it accepts to listen this source.
		sources = eventsForTarget.acceptedSources;
		for (i = 0; i < sources.length; i++)
		{
			if (sources[i]['eventSrc'] == source)
			{
				wantReceive = true;
				handler = sources[i]['eventHandler'];
				break;
			}
		}
	}
	
	if (wantReceive && handler)
	{
		handler({eventType: eventType, eventData: eventData});
	}
}

// - Event dispatcher constants.
EventDispatcher.EVENT_SOURCE_PORTAL		= 'http://palette.ercim.org/ns/portal';
EventDispatcher.EVENT_WIDGET_DESTROYED	= 'http://palette.ercim.org/ns/event/destroyed';
EventDispatcher.EVENT_WIDGET_MINIMIZED	= 'http://palette.ercim.org/ns/event/minimized';
EventDispatcher.EVENT_WIDGET_MAXIMIZED	= 'http://palette.ercim.org/ns/event/maximized';